package net.sf.borg.control.systemLogin;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

import net.sf.borg.model.beans.User;
import net.sf.borg.model.db.jdbc.JdbcDB;
import net.sf.borg.model.db.jdbc.SystemDB;
import net.sf.borg.ui.systemLogin.NameLoginView;

/**
 * Class BorgNameLoginSystem
 * 
 * The BorgNameLoginSystem is used to log in a single user to a single
 * SystemDB.  Use method runLogin() to require the user to log in, and
 * use the boolean return from runLogin() to detect whether or not the
 * login was a success.
 * 
 * The BorgNameLoginSystem allows for querying of the currently logged
 * user, as well as the ability to set a new SystemDB.  If a new SystemDB
 * is set, the username in this instance will be nullified.
 * 
 * @author Matthew Carroll
 */

public class BorgNameLoginSystem implements LoginSystem {
	
	//Singleton support
	static private BorgNameLoginSystem instance = null;
	
	static public BorgNameLoginSystem getInstance() {
		if(instance == null) {
			instance = new BorgNameLoginSystem();
		}
		return instance;
	}

	private SystemDB 	systemDB;	//Access to database holding table of usernames
	private String		username;	//Username either created or selected in the login process
	private LoginState	loginState = LoginState.AUTOMATIC;	//Initially we will automatically decide
															//which login view the user sees
	private enum ErrMsg { 
		INVALID_ID ("That ID is invalid, please choose a non-blank username."),
		ID_ALREADY_EXISTS ("That ID already exists, please choose another."); 
	
		private String msg = "";
		
		private ErrMsg(String msg) {
			this.msg = msg;
		}
		
		public String toString() {
			return this.msg;
		}
	};
	
	private BorgNameLoginSystem() {
		systemDB = null;
		username = null;
	}
	
	public String getUsername() {
		return username;
	}
	

	public void setDB(SystemDB db) {
		if(db != systemDB) {
			username = null;
		}
		systemDB = db;
	}
	
	public void setLoginState(LoginState state) {
		loginState = state;
	}
	
	private boolean validID(String id) {
		if(id.trim().length() > 0) { //non-empty, non-blank id
			return true;
		}else{
			return false;
		}
	}
	
	private void showBadLoginMsg(ErrMsg err) 
	{
		JOptionPane.showMessageDialog(null,
			    err,
			    "Inane error",
			    JOptionPane.ERROR_MESSAGE);
	}

	/*
	 * Supplies GUI and business logic to require a user to
	 * log in based on a SystemDB.  The only way this method
	 * returns FALSE is if the user cancels the log in process.
	 * Otherwise this method retries user input until it matches
	 * a SystemDB username.
	 * 
	 * If the login is a success, then the global database is changed
	 * to that particular user's database.
	 */
	public boolean runLogin() 
	{		
		boolean loginSuccess = false;
		boolean loginDone = false;
		String userDBPath = "";
		
		try{			
			//collect all users
			PreparedStatement stmt = systemDB.getPSAll();
			ResultSet rs = stmt.executeQuery();
			List<String> names = new ArrayList<String>();
			
			while(rs.next()){
				names.add(rs.getString("user_name"));
			}	

			
			//----- GUI Prompt ------
			//If there are no users then prompt to make one
			//Else, prompt to choose a user
			NameLoginView loginView;
			
			//Show a dialog requesting that the user login:
			if(loginState == LoginState.NEWUSER || (loginState == LoginState.AUTOMATIC && names.size() == 0)) {
				loginView = new NameLoginView(NameLoginView.LoginState.CREATE_USER_STATE, names);
			}else{
				loginView = new NameLoginView(NameLoginView.LoginState.CHOOSE_USER_STATE, names);
			}
				
			//query username until we get a valid name, or user cancels
			String newName;
			do{
				//get id from user
				newName = loginView.runLoginView();
				
				//update our knowledge of the login state;
				if(loginView.getState() == NameLoginView.LoginState.CREATE_USER_STATE) {
					loginState = LoginState.NEWUSER;
				}else{
					loginState = LoginState.CHOOSEUSER;
				}
				
				//if user did NOT cancel
				if( newName != null ) 
				{	
					if( !names.contains(newName) ) { // the id does not exist yet
						if(validID(newName)) { // the id is valid -> good to go
							//add username to database
							systemDB.addObj(new User(newName, systemDB.nextkey()));
							loginSuccess = true;
							loginDone = true;
							username = newName;
						}else{ //the id is invalid -> bad input, try again
							loginSuccess = false;
							loginDone = false;
							//System.out.println("invalid name");
							showBadLoginMsg(ErrMsg.INVALID_ID);
						}
					}else if(loginState == LoginState.NEWUSER) { //the username already exists, and we're trying to create it -> bad input, try again
						loginSuccess = false;
						loginDone = false;
						//System.out.println("already used");
						showBadLoginMsg(ErrMsg.ID_ALREADY_EXISTS);
					}else if(loginState == LoginState.CHOOSEUSER) { //correctly chose an existing id -> good to go
						loginSuccess = true;
						loginDone = true;
						username = newName;
					}
				}else{ //login failed because user canceled -> good to go
					loginSuccess = false;
					loginDone = true;
				}
			}while( !loginSuccess && !loginDone ); //until login succeeds, or until user cancels
			
		}
		catch(Exception e){
			System.out.println("[BorgNameLoginSystem] - Error when looking up users: " + e);
		}
		
		if(loginSuccess) {
			//load specified user's db
			JdbcDB.setDBPath(userDBPath);
		}
		
		return loginSuccess;
	}

}
